﻿using System;
using System.Net;
using System.Net.Sockets;

namespace Narwhunderful.Aphelia.Framework.Net
{

    public class NarsockServer : BaseNarsock
    {
        public event EventHandler<NarsockConnectionRequestEventArgs> ConnectionRequest;
        public event EventHandler<NarsockDataArrivalEventArgs> ClientDataArrival;
        public event EventHandler<NarsockSendEventArgs> ClientSendProgress;
        public event EventHandler<NarsockSendEventArgs> ClientSendComplete;
        public event EventHandler<NarsockStateChangedEventArgs> ClientStateChanged;
        public event EventHandler<NarsockExceptionEventArgs> ClientExceptionReceived;

        Socket _socket2 = null;

        int _maxPendingConnections = 1;
        public int MaxPendingConnections
        {
            get
            {
                return _maxPendingConnections;
            }
            set
            {
                _maxPendingConnections = value;
            }
        }

        public void Listen()
        {
            DoListen();
        }

        protected void DoListen()
        {
            OnStateChanged(new NarsockStateChangedEventArgs(NarsockState.Listening, base.State));
            try
            {
                if (Socket.OSSupportsIPv4)
                {
                    base.Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, Port);
                    base.Socket.Bind(ipLocal);
                    base.Socket.Listen(_maxPendingConnections);
                    base.Socket.BeginAccept(new AsyncCallback(ListenCallback), base.Socket);
                }
                if (Socket.OSSupportsIPv6)
                {
                    _socket2 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
                    IPEndPoint ipLocal = new IPEndPoint(IPAddress.IPv6Any, Port);
                    _socket2.Bind(ipLocal);
                    _socket2.Listen(_maxPendingConnections);
                    _socket2.BeginAccept(new AsyncCallback(ListenCallback), _socket2);
                }
            }
            catch (SocketException ex)
            {
                OnExceptionReceived(new NarsockExceptionEventArgs(ex));
            }
        }

        protected virtual void ListenCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                if (socket == null) return;
                OnConnectionRequest(new NarsockConnectionRequestEventArgs(socket.EndAccept(ar)));
                if (base.State != NarsockState.Listening)
                {
                    socket.Close();
                }
                else
                {
                    socket.BeginAccept(new AsyncCallback(this.ListenCallback), socket);
                }
            }
            catch (SocketException ex)
            {
                if (ex.SocketErrorCode != SocketError.Success)
                {
                    OnExceptionReceived(new NarsockExceptionEventArgs(ex));
                }
            }
            catch (ObjectDisposedException ex1)
            {
                if (ex1 != null) return;
            }
        }

        protected internal virtual void OnConnectionRequest(NarsockConnectionRequestEventArgs e)
        {
            ConnectionRequest.SafeInvoke(this, e);
            if (e.Cancel)
            {
                e.ClientSocket.Disconnect(false);
                e.ClientSocket.Close();
            }
        }

        protected virtual void OnClientDataArrival(NarsockDataArrivalEventArgs e)
        {
            ClientDataArrival.SafeCrossThreadInvoke(this, e);
        }

        internal virtual void OnClientDataArrival(object sender, NarsockDataArrivalEventArgs e)
        {
            ClientDataArrival.SafeCrossThreadInvoke(sender, e);
        }

        protected virtual void OnClientSendComplete(NarsockSendEventArgs e)
        {
            ClientSendComplete.SafeCrossThreadInvoke(this, e);
        }

        internal virtual void OnClientSendComplete(object sender, NarsockSendEventArgs e)
        {
            ClientSendComplete.SafeCrossThreadInvoke(sender, e);
        }

        protected virtual void OnClientSendProgress(NarsockSendEventArgs e)
        {
            ClientSendProgress.SafeCrossThreadInvoke(this, e);
        }

        internal virtual void OnClientSendProgress(object sender, NarsockSendEventArgs e)
        {
            ClientSendProgress.SafeCrossThreadInvoke(sender, e);
        }

        protected virtual void OnClientStateChanged(NarsockStateChangedEventArgs e)
        {
            ClientStateChanged.SafeCrossThreadInvoke(this, e);
        }

        internal virtual void OnClientStateChanged(object sender, NarsockStateChangedEventArgs e)
        {
            ClientStateChanged.SafeCrossThreadInvoke(sender, e);
        }

        protected virtual void OnClientExceptionReceived(NarsockExceptionEventArgs e)
        {
            ClientExceptionReceived.SafeCrossThreadInvoke(this, e);
        }

        internal virtual void OnClientExceptionReceived(object sender, NarsockExceptionEventArgs e)
        {
            ClientExceptionReceived.SafeCrossThreadInvoke(sender, e);
        }

    }

}
